package com.tj.jinzao;

import android.content.Context;
import android.os.Build;
import android.os.Environment;

import com.tj.jinzao.utils.Trace;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 异常日志生成于：
 * - /sdcard/Log
 * <p/>
 * 子类可重写collectProductInfo加入更多关于产品的信息
 * <p/>
 * 通常在Application的onCreate方法中实例化
 */
public class DefaultUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    protected final DateFormat mDateFormat;
    private final Thread.UncaughtExceptionHandler mDefaultHandler;
    private final Map<String, String> mDeviceInfo;
    protected final Context mApplicationContext;
    protected final String mDir;

    public DefaultUncaughtExceptionHandler(Context context, String dir) {
        mDir = dir;
        mApplicationContext = context.getApplicationContext();
        mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss");
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        mDeviceInfo = new LinkedHashMap();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {
        if (!handleException(thread, throwable)) {
            Trace.flushLocked();
            mDefaultHandler.uncaughtException(thread, throwable);
        } else {
            try {
                Thread.sleep(1500);
            } catch (InterruptedException ignored) {
            }

            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(-1);
        }
    }

    protected void collectProductInfo(Map<String, String> info) {
        mDeviceInfo.put("VersionName", BuildConfig.VERSION_NAME);
        mDeviceInfo.put("VersionCode", String.valueOf(BuildConfig.VERSION_CODE));
        mDeviceInfo.put("PackageName", BuildConfig.APPLICATION_ID);
    }

    public boolean handleException(Thread thread, Throwable throwable) {
        collectDeviceInfo();
        saveStackTrace(throwable);
        // Always return false so the default handler can show up
        return false;
    }

    private void collectDeviceInfo() {
        try {
            mDeviceInfo.put("[Product]", "");
            collectProductInfo(mDeviceInfo);
        } catch (Exception ignored) {
        }
        mDeviceInfo.put("[Device]", "");
        Field[] fields = Build.class.getFields();
        for (Field field : fields) {
            try {
                mDeviceInfo.put(field.getName(), field.get(null).toString());
            } catch (Exception ignored) {
            }
        }
    }

    protected void saveStackTrace(Throwable throwable) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : mDeviceInfo.entrySet()) {
            sb.append(entry.getKey()).append('=').append(entry.getValue()).append("\n");
        }
        sb.append("\n[Exception]\n");

        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        throwable.printStackTrace(printWriter);
        Throwable cause = throwable.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();

        String stackTrace = writer.toString();
        sb.append(stackTrace);

        String fileName = "crash-" + mDateFormat.format(new Date()) + ".txt";
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            File dir = new File(mDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            saveStackTrace(sb, dir, fileName);
        } else {
            saveStackTrace(sb, mApplicationContext.getFilesDir(), fileName);
        }

        countErr(stackTrace);
    }

    private void saveStackTrace(StringBuilder stackTrace, File path, String name) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File(path, name));
            fos.write(stackTrace.toString().getBytes());
        } catch (Throwable ignored) {
        } finally {
            if (null != fos) {
                try {
                    fos.close();
                } catch (Throwable ignored) {
                }
            }
        }
    }

    protected void countErr(@SuppressWarnings("UnusedParameters") String stackTrace) {
    }
}
